﻿using CTM.Web.Filters;
using CTM.Web.Models;
using CTM.Web.Models.Input;
using CTM.Web.Models.View.Note;
using CTM.Web.Services;
using CTM.Web.Utils;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Mvc;

namespace CTM.Web.Controllers
{
    public class NoteController : Controller
    {
        private IPostService postService;
        private IAccountService accountService;
        private ICategoryService categoryService;
        private ITagService tagService;
        private IOrgService orgService;
        private ApplicationUserManager userManager;

        public ApplicationUserManager UserManager
        {
            get
            {
                return userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
            }
            private set
            {
                userManager = value;
            }
        }

        public NoteController(IPostService postService, IAccountService accountService,
             ICategoryService categoryService, ITagService tagService, IOrgService orgService)
        {
            this.postService = postService;
            this.accountService = accountService;
            this.categoryService = categoryService;
            this.tagService = tagService;
            this.orgService = orgService;
        }

        #region Action

        public ActionResult Index()
        {
            //var conversations = conversationService.Get().Select(x => new NoteViewModel(x));
            //return View(conversations.ToList());
            return View();
        }

        public ActionResult Details(Guid? id)
        {
            //if (id == null)
            //{
            //    return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            //}
            //Note conversation = conversationService.GetById(id);
            //if (conversation == null)
            //{
            //    return HttpNotFound();
            //}
            //var model = new NoteViewModel(conversation);
            //return View(model);
            throw new NotImplementedException();
        }

        public ActionResult Create()
        {
            return View();
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        [ApplicationAuthorize(Permissions = Rights.Create + Modules.Conversation)]
        public ActionResult Create(NoteViewModel createModel)
        {
            // Clear some validations since they're not necessary
            ModelState.Remove("Id");
            ModelState.Remove("Category");
            ModelState.Remove("Tags");

            ApplicationUser giver = null;
            ApplicationUser receiver = null;
            switch (Request.Form["Category"])
            {
                case "MyNote":
                    giver = accountService.GetById(GlobalVariables.CurrentUser.Id);
                    receiver = accountService.GetById(GlobalVariables.CurrentUser.Id);
                    break;

                case "ManagerNote":
                    giver = accountService.GetById(GlobalVariables.CurrentUser.Id);
                    receiver = accountService.GetById(GlobalVariables.SelectedUser.Id);
                    break;

                default:
                    ModelState.AddModelError("Category", "Invalid category.");
                    break;
            }

            if (ModelState.IsValid)
            {
                var note = new Post
                {
                    Id = Guid.NewGuid(),
                    Title = Request.Form["Category"],
                    Description = createModel.Description,
                    Category = categoryService.GetByName("Note"),
                    IsDeleted = false,
                    CreatedOn = DateTime.UtcNow,
                    CreatedBy = GlobalVariables.CurrentUser.Id,
                    Manager = giver,
                    Employee = receiver,
                };

                var tags = Request.Form["Tags"].Split(',');
                for (int i = 0; i < tags.Length; i++)
                {
                    if (string.IsNullOrWhiteSpace(tags[i])) continue;

                    string tagName = tags[i];
                    var tag = tagService.GetByName(tagName);
                    if (tag != null)
                    {
                        if (!note.Tags.Contains(tag))
                        {
                            note.Tags.Add(tag);
                        }
                    }
                    else
                    {
                        tag = new Tag
                        {
                            Name = tagName
                        };
                        tagService.Insert(tag);
                        note.Tags.Add(tag);
                    }
                }

                postService.Insert(note);
            }

            var result = new
            {
                success = ModelState.IsValid,
                message = string.Join("\n", ModelState.SelectMany(s => s.Value.Errors).Select(e => e.ErrorMessage))
            };
            return Json(result);
        }

        public ActionResult Edit(Guid? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }

            var note = postService.GetById(id);
            if (note == null || note.IsDeleted)
            {
                return HttpNotFound();
            }

            var model = new NoteViewModel(note);
            return View(model);
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        [ApplicationAuthorize(Permissions = Rights.Modify + Modules.Conversation)]
        public ActionResult Edit(NoteViewModel editModel)
        {
            // Clear validation for Id, Title, Type & Emoticon since it's not necessary
            ModelState.Remove("Title");
            ModelState.Remove("Type");
            ModelState.Remove("Emoticon");
            ModelState.Remove("Category");
            ModelState.Remove("Tags");

            if (ModelState.IsValid)
            {
                var note = postService.GetById(editModel.Id);
                note.Description = editModel.Description;
                note.ModifiedOn = DateTime.UtcNow;
                note.ModifiedBy = GlobalVariables.CurrentUser.Id;

                note.Tags.Clear();
                var tags = Request.Form["Tags"].Split(',');
                for (int i = 0; i < tags.Length; i++)
                {
                    if (string.IsNullOrWhiteSpace(tags[i])) continue;

                    string tagName = tags[i];
                    var tag = tagService.GetByName(tagName);
                    if (tag != null)
                    {
                        if (!note.Tags.Contains(tag))
                        {
                            note.Tags.Add(tag);
                        }
                    }
                    else
                    {
                        tag = new Tag
                        {
                            Name = tagName
                        };
                        tagService.Insert(tag);
                        note.Tags.Add(tag);
                    }
                }

                postService.Update(note);
            }

            var result = new
            {
                success = ModelState.IsValid,
                message = string.Join("\n", ModelState.SelectMany(s => s.Value.Errors).Select(e => e.ErrorMessage))
            };
            return Json(result);
        }

        [ApplicationAuthorize(Permissions = Rights.Delete + Modules.Conversation)]
        public ActionResult Delete(Guid? id, string type)
        {
            if (!id.HasValue) return new HttpStatusCodeResult(HttpStatusCode.BadRequest);

            var note = postService.GetById(id);
            if (note == null) return HttpNotFound();

            // Delete note
            postService.Delete(note);

            return RedirectToAction(type, new { id = GlobalVariables.SelectedUser.Id });
        }

        [ApplicationAuthorize(Permissions = Rights.View + Modules.Conversation)]
        public ActionResult ManagerNote(ManagerNoteSearchModel searchModel)
        {
            if (!ApplicationHelpers.IsManager(accountService, UserManager, GlobalVariables.CurrentUser.Id, GlobalVariables.SelectedUser.Id)
                && GlobalVariables.CurrentUser.Id != GlobalVariables.SelectedUser.Id)
            {
                return Redirect(Url.Action("Index", "Feedback", new { id = GlobalVariables.SelectedUser.Id }));
            }

            // The manager can add note for his/her subordinates
            // If the current user is not a manager, his/her ManageOrgs will be empty
            string userId = GlobalVariables.CurrentUser.Id;
            if (!string.IsNullOrEmpty(searchModel.Id) && userId != searchModel.Id)
            {
                var manager = accountService.GetById(userId);
                var user = accountService.GetById(searchModel.Id);

                if (ApplicationHelpers.IsManager(accountService, UserManager, userId, searchModel.Id))
                {
                    ViewBag.CanAddNote = true;
                }
            }

            var viewModel = new ManagerNoteViewModel();
            viewModel.Year = searchModel.Year;
            viewModel.Keyword = searchModel.Keyword;
            viewModel.Notes = SearchManagerNotes(searchModel);
            viewModel.Tags = viewModel.Notes.SelectMany(n => n.Tags).Select(t => t.Name).Distinct();
            return View(viewModel);
        }

        [ApplicationAuthorize(Permissions = Rights.View + Modules.Conversation)]
        public ActionResult MyNote(MyNoteSearchModel searchModel)
        {
            if (!ApplicationHelpers.IsManager(accountService, UserManager, GlobalVariables.CurrentUser.Id, GlobalVariables.SelectedUser.Id)
                && GlobalVariables.CurrentUser.Id != GlobalVariables.SelectedUser.Id)
            {
                return Redirect(Url.Action("Index", "Feedback", new { id = GlobalVariables.SelectedUser.Id }));
            }

            var viewModel = new MyNoteViewModel();
            viewModel.Year = searchModel.Year;
            viewModel.Keyword = searchModel.Keyword;
            viewModel.Notes = SearchMyNotes(searchModel);
            viewModel.Tags = viewModel.Notes.SelectMany(n => n.Tags).Select(t => t.Name).Distinct();
            return View(viewModel);
        }

        #endregion

        #region Method

        private IEnumerable<NoteViewModel> SearchManagerNotes(ManagerNoteSearchModel searchModel)
        {
            // Get the current user
            var currentUser = accountService.GetById(GlobalVariables.CurrentUser.Id);

            // Get all manager notes
            var noteQuery = postService.Get(p => p.Category.Name == "Note" && p.Manager.Id != p.Employee.Id);

            // Search by user id & year
            noteQuery = noteQuery.Where(n => n.Employee.Id == GlobalVariables.SelectedUser.Id);
            noteQuery = noteQuery.Where(n => n.CreatedOn.Year == searchModel.Year);

            // Search by keyword in title, description & tags (case-insensitive)
            // The result is the combination of every record that matches
            if (!string.IsNullOrWhiteSpace(searchModel.Keyword))
            {
                // Normalize the keyword
                searchModel.Keyword = searchModel.Keyword.Trim();
                searchModel.Keyword = searchModel.Keyword.ToUpper();

                noteQuery = noteQuery.Where(n => n.Title.ToUpper().Contains(searchModel.Keyword)
                    || n.Description.ToUpper().Contains(searchModel.Keyword)
                    || n.Tags.Any(t => t.Name.ToUpper().Contains(searchModel.Keyword)));
            }

            if (ApplicationHelpers.IsManager(accountService, UserManager, GlobalVariables.CurrentUser.Id, GlobalVariables.SelectedUser.Id))
            {
                // The manager can only see his/her own notes for the viewing user
                return noteQuery.Where(n => n.Manager.Id == currentUser.Id)
                    .Select(n => new NoteViewModel(n))
                    .OrderByDescending(n => n.CreatedOn);
            }
            else
            {
                // Other users will see nothing
                return Enumerable.Empty<NoteViewModel>();
            }
        }

        private IEnumerable<NoteViewModel> SearchMyNotes(MyNoteSearchModel searchModel)
        {
            // Get the current user
            var currentUser = accountService.GetById(GlobalVariables.CurrentUser.Id);

            // Get all personal notes
            var noteQuery = postService.Get(p => p.Category.Name == "Note" && p.Manager.Id == p.Employee.Id);

            // Search by user id & year
            noteQuery = noteQuery.Where(n => n.Employee.Id == GlobalVariables.CurrentUser.Id);
            noteQuery = noteQuery.Where(n => n.CreatedOn.Year == searchModel.Year);

            // Search by keyword in title, description & tags (case-insensitive)
            // The result is the combination of every record that matches
            if (!string.IsNullOrWhiteSpace(searchModel.Keyword))
            {
                // Normalize the keyword
                searchModel.Keyword = searchModel.Keyword.Trim();
                searchModel.Keyword = searchModel.Keyword.ToUpper();

                noteQuery = noteQuery.Where(n => n.Title.ToUpper().Contains(searchModel.Keyword)
                    || n.Description.ToUpper().Contains(searchModel.Keyword)
                    || n.Tags.Any(t => t.Name.ToUpper().Contains(searchModel.Keyword)));
            }

            if (GlobalVariables.CurrentUser.Id == GlobalVariables.SelectedUser.Id)
            {
                // The user can see his/her own notes
                return noteQuery.Where(n => n.CreatedBy == currentUser.Id)
                    .Select(n => new NoteViewModel(n))
                    .OrderByDescending(n => n.CreatedOn);
            }
            else
            {
                // Other users will see nothing
                return Enumerable.Empty<NoteViewModel>();
            }
        }

        #endregion

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (userManager != null)
                {
                    userManager.Dispose();
                    userManager = null;
                }
            }

            base.Dispose(disposing);
        }
    }
}